import { isFunction } from '@material/helpers';
import { MessageInfo, SendApiResult, SendParams } from '../type';
import { Message } from '..';
import { isString } from '@tarojs/shared';

export interface MessagePluginManager {
    name: string;
    initialize?: (manager: PluginManager) => void;
    beforeSendAsync?: (params: SendParams, context: Message) => Promise<SendParams | boolean>;
    beforeSend?: (params: SendParams, context: Message) => SendParams | boolean;
    startSendAsync?: (params: SendParams, context: Message) => Promise<SendParams | boolean>;
    startSend?: (params: SendParams, context: Message) => SendParams | boolean;
    afterSendAsync?: (response: [0 | 1, SendApiResult], context: Message) => Promise<void>;
    afterSend?: (response: [0 | 1, SendApiResult], context: Message) => void;
    beforeReceive?: (info: MessageInfo, context: Message) => MessageInfo | boolean;
    afterReceive?: (info: MessageInfo, context: Message) => void;
    beforeDestroy?: (context: Message) => void;
}
export type HookNameType = keyof Omit<MessagePluginManager, 'name' | 'initialize'>;
export type HookNameCanAsyncType = 'beforeSend' | 'startSend' | 'afterSend';
export class PluginManager {
    private plugins: Map<string, MessagePluginManager> = new Map();
    constructor() {
        this.plugins = new Map();
    }

    registerPlugin(plugin: MessagePluginManager): void {
        if (this.plugins.has(plugin.name)) {
            console.warn(`Plugin ${plugin.name} already registered`);
            return;
        }
        this.plugins.set(plugin.name, plugin);
        isFunction(plugin.initialize) && plugin.initialize(this);
    }

    unregisterPlugin(pluginName: string): void {
        pluginName && this.plugins.delete(pluginName);
    }

    getPlugin(pluginName: string): MessagePluginManager | undefined {
        return this.plugins.get(pluginName);
    }

    getPlugins(): MessagePluginManager[] {
        return Array.from(this.plugins.values());
    }
    hasHook(hookName: HookNameType) {
        if (isString(hookName) && this.plugins.has(hookName)) {
            const hook = this.getPlugin(hookName);
            if (isFunction(hook)) {
                return true;
            }
            return false;
        }
        return false;
    }

    // 执行钩子函数
    executeHook(hookName: HookNameType, initialValue: any, context: Message) {
        let value = initialValue;
        try {
            for (const plugin of this.plugins.values()) {
                const isHasHook = plugin && typeof plugin[hookName] === 'function';
                if (!isHasHook) continue;
                const result = (plugin[hookName] as (...args: any[]) => any)(initialValue, context);
                if (result === false) {
                    return false;
                }
                if (result !== undefined) {
                    value = result;
                }
            }
        } catch (err) {
            return value;
        }
        return value;
    }
}
